package cn.changeforyou.web.cloud.devUtilApi.exception;

import cn.changeforyou.base.exception.JsonException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;

/**
 * @author zhyu
 * @md-model springboot.mvc.exception 全局异常处理
 */
@ControllerAdvice
public class GlobalExceptionHandler2 {

      Logger log = LoggerFactory.getLogger("exception");

      @ResponseBody
      @ExceptionHandler(value = JsonException.class)
      public ModelAndView handleException(JsonException e) {
            if (log.isDebugEnabled()) {
                  e.printStackTrace();
            }
            ModelAndView mv = new ModelAndView();
            MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
            mv.setView(jsonView);
            mv.addObject("code", e.getCode());
            mv.addObject("message", e.getMessage());
            return mv;
      }

      @ResponseBody
      @ExceptionHandler(value = Exception.class)
      public ModelAndView handleException(Exception e, HandlerMethod method) {
            ModelAndView mv = new ModelAndView();
            boolean restController = AnnotatedElementUtils.hasAnnotation(method.getMethod().getDeclaringClass(), RestController.class);
            ResponseBody responseBody = method.getMethod().getAnnotation(ResponseBody.class);

            if (restController || null != responseBody) {
                  //这时需要返回 json
                  MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
                  mv.setView(jsonView);
                  if (e instanceof BindException) {
                        BindingResult bindingResult;
                        if (e instanceof MethodArgumentNotValidException) {
                              bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
                        } else {
                              bindingResult = ((BindException) e).getBindingResult();
                        }
                        FieldError fieldError = bindingResult.getFieldError();
                        if (null != fieldError) {
                              String field = fieldError.getField();
                              String defaultMessage = fieldError.getDefaultMessage();
                              String msg = "参数" + field + "异常,  " + defaultMessage;
                              mv.addObject("message", msg);
                              mv.addObject("code", 400);
                        } else if (null != bindingResult.getGlobalError()) {
                              ObjectError globalError = bindingResult.getGlobalError();
                              String msg = "参数异常,  " + globalError.getDefaultMessage();
                              mv.addObject("message", msg);
                              mv.addObject("code", 400);
                        } else {
                              mv.addObject("message", "参数绑定异常");
                              mv.addObject("code", 400);
                        }
                  }
            } else if (e instanceof HttpMessageNotReadableException) {
                  Throwable cause = e.getCause();
                  //注意 InvalidFormatException 是 MismatchedInputException 的子类
                  if (cause instanceof InvalidFormatException) {
                        InvalidFormatException invalidFormatException = (InvalidFormatException) cause;
                        wrapperJsonMappingException(mv, invalidFormatException, "的数据类型错误, 应该是" + invalidFormatException.getTargetType().getSimpleName());
                  } else if (cause instanceof MismatchedInputException) {
                        MismatchedInputException mismatchedInputException = (MismatchedInputException) cause;
                        wrapperJsonMappingException(mv, mismatchedInputException, "应该用嵌套json对象或者数组来传递");
                  } else {
                        mv.addObject("code", 400);
                        mv.addObject("message", "参数类型错误");
                  }
            } else if (e instanceof MissingServletRequestParameterException) {
                  String parameterName = ((MissingServletRequestParameterException) e).getParameterName();
                  String msg = "参数" + parameterName + "没填写, 或者请后端检查是否是json的情况下把@RequestBody写成@RequestParam";
                  mv.addObject("message", msg);
                  mv.addObject("code", 400);
            } else if (e instanceof DuplicateKeyException) {
                  mv.addObject("message", "数据请勿反复提交和修改");
                  mv.addObject("code", 400);
            }
            if (!mv.getModel().containsKey("code")) {
                  System.err.println("发现没有没有录入的参数异常, 请检查代码");
                  log.error("error", e);
                  mv.addObject("code", 400);
                  mv.addObject("message", "参数绑定异常");
            }
            return mv;
      }

      final static Pattern PATTERN = Pattern.compile("\\[\"([a-zA-Z0-9]*)\"\\]");

      private void wrapperJsonMappingException(ModelAndView mv, JsonMappingException jsonMappingException, String message) {
            String pathReference = jsonMappingException.getPathReference();
            Matcher matcher = PATTERN.matcher(pathReference);
            StringBuilder sb = new StringBuilder();
            while (matcher.find()) {
                  String group = matcher.group(1);
                  sb.append(group).append(".");
            }
            sb.deleteCharAt(sb.length() - 1);
            mv.addObject("code", 400);
            mv.addObject("message", "参数" + sb + message);
      }
}